package com.kotlinvben.filter

import com.fasterxml.jackson.databind.ObjectMapper
import com.kotlinvben.bean.Result
import com.kotlinvben.exception.BusinessErrorCode
import com.kotlinvben.service.UserCacheService
import io.jsonwebtoken.ExpiredJwtException
import io.jsonwebtoken.Jwts
import io.jsonwebtoken.security.Keys
import jakarta.servlet.FilterChain
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Value
import org.springframework.http.MediaType
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.stereotype.Component
import org.springframework.web.filter.OncePerRequestFilter
import java.nio.charset.StandardCharsets

@Component
class JwtAuthenticationFilter(
    private val userCacheService: UserCacheService
) : OncePerRequestFilter() {

    private val log = LoggerFactory.getLogger(JwtAuthenticationFilter::class.java)

    @Value("\${jwt.secret}")
    private lateinit var jwtSecret: String

    private val objectMapper = ObjectMapper()

    override fun doFilterInternal(
        request: HttpServletRequest,
        response: HttpServletResponse,
        filterChain: FilterChain
    ) {
        log.trace("Processing request: ${request.method} ${request.requestURI}")

        // 获取 Authorization 头
        val authHeader = request.getHeader("Authorization")
        if (authHeader == null || !authHeader.startsWith("Bearer ")) {
            log.info("${request.requestURI} No valid Authorization header found, continuing filter chain")
            filterChain.doFilter(request, response)
            return
        }

        // 提取 token
        val token = authHeader.substring(7)
        log.trace("Extracted JWT token: ${token.take(10)}...")

        try {
            // 验证 token
            val key = Keys.hmacShaKeyFor(jwtSecret.toByteArray(StandardCharsets.UTF_8))
            val claimsJws = Jwts.parserBuilder()
                .setSigningKey(key)
                .build()
                .parseClaimsJws(token)

            // 从 token 中获取用户信息
            val userId = claimsJws.body["userId"] as Int
            val username = claimsJws.body["username"] as String

            log.trace("Token validated successfully for user: $username (ID: $userId)")

            // 从缓存中获取用户信息
            val currentUser = userCacheService.getUserById(userId.toLong())
            if (currentUser == null) {
                log.warn("${request.requestURI} User not found in cache: $userId")
                handleUnauthorized(response, "User not found")
                return
            }

            // 创建认证信息
            val roles = currentUser.roles
            val authorities = roles.map { SimpleGrantedAuthority(it.code) }
            val authentication = UsernamePasswordAuthenticationToken(
                currentUser,
                null,
                authorities
            )

            // 设置到 SecurityContext
            SecurityContextHolder.getContext().authentication = authentication
            log.trace("Authentication set in SecurityContext for user: $username with roles: $roles")

            filterChain.doFilter(request, response)
        } catch (e: ExpiredJwtException) {
            log.warn("Token expired: ${e.message}")
            handleUnauthorized(response, "Token has expired")
        } catch (e: Exception) {
            log.error("Token validation failed: ${e.message}", e)
            handleUnauthorized(response, "Invalid token")
        }
    }

    private fun handleUnauthorized(response: HttpServletResponse, message: String) {
        log.warn("Unauthorized access: $message")
        response.status = HttpServletResponse.SC_UNAUTHORIZED
        response.contentType = MediaType.APPLICATION_JSON_VALUE
        response.characterEncoding = "UTF-8"

        val result = Result.error<Unit>(BusinessErrorCode.UNAUTHORIZED, message)
        response.writer.write(objectMapper.writeValueAsString(result))
    }
} 